package com.mes.cloud.base;

import com.mes.cloud.commons.CommonUtil;
import com.mes.cloud.mail.Mail;
import com.mes.cloud.mail.MailService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

@Service
@RefreshScope
public class ErrorNotifier {

    private static final Logger LOGGER = LoggerFactory.getLogger(ErrorNotifier.class);
    // 邮件错误通知主题
    private static final String ERROR_MAIL_SUBJECT_FORMAT = "[%s][%s]错误通知";
    // 错误描述
    private static final String COMMON_ERROR_DESC = "服务运行错误";
    // 接收者
    @Value("${mes.cloud.error.recipients:}")
    String recipients;
    // 忽略的手机对象
    @Value("${mes.cloud.error.mobile.ignored:}")
    String mobileIgnored;
    // 忽略的邮件对象
    @Value("${mes.cloud.error.mail.ignored:}")
    String mailIgnored;

    @Autowired
    MailService mailService;

    /*@Autowired
    SMSClient smsClient;*/

    @Autowired
    ServiceInfo serviceInfo;
    // 邮件接收者
    String mailRecipients;
    // 手机接收者
    String mobileRecipients;

    Set<String> mobileIgnoredSet = new HashSet<>();

    Set<String> mailIgnoredSet = new HashSet<>();

    @PostConstruct
    private void init() {
        initRecipients();
        initIgnored();
    }

    private void initRecipients() {
        Set<String> mails = new HashSet<>();
        Set<String> mobiles = new HashSet<>();

        if (!StringUtils.isBlank(recipients)) {
            Arrays.stream(recipients.split(",")).forEach(recipient -> {
                recipient = recipient.trim();
                if (StringUtils.isBlank(recipient)) return;

                if (recipient.contains("@")) {
                    if (CommonUtil.validateEmail(recipient)) {
                        mails.add(recipient);
                        return;
                    }
                } else {
                    if (CommonUtil.validateMobile(recipient)) {
                        mobiles.add(recipient);
                        return;
                    }
                }
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.warn("Invalid recipient. {}", recipient);
                }
            });
        }

        mailRecipients = StringUtils.join(mails, ",");
        mobileRecipients = StringUtils.join(mobiles, ",");
    }

    private void initIgnored() {
        if (!StringUtils.isBlank(mobileIgnored)) {
            Arrays.stream(mobileIgnored.split(",")).forEach(ignored -> addIgnored(mobileIgnoredSet, ignored));
        }
        if (!StringUtils.isBlank(mailIgnored)) {
            Arrays.stream(mailIgnored.split(",")).forEach(ignored -> addIgnored(mailIgnoredSet, ignored));
        }
    }

    private void addIgnored(Set<String> ignoredSet, String ignored) {
        if (!StringUtils.isBlank(ignored)) {
            ignoredSet.add(ignored);
        }
    }

    private boolean isIgnored(Set<String> ignoredSet, Throwable throwable) {
        if (throwable == null) return true;

        if (ignoredSet.isEmpty()) return false;

        String className = throwable.getClass().getName();
        String simpleName = throwable.getClass().getSimpleName();

        if (ignoredSet.contains(className.toLowerCase()) || ignoredSet.contains(simpleName.toLowerCase())) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.debug("####Throwable is ignored.{}", className);
            }
            return true;
        }

        return false;
    }


    @Async
    public void notifyByMail(Throwable throwable, String message) {
        if (isIgnored(mailIgnoredSet, throwable)) return;

        if (StringUtils.isEmpty(mailRecipients)) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.debug("####Mail recipients is empty.");
            }
            return;
        }

        StringWriter sw = new StringWriter();
        sw.append(message).append("<br/><br/>");
        throwable.printStackTrace(new PrintWriter(sw, true));

        mailService.sendMail(Mail.of(mailRecipients,
                String.format(ERROR_MAIL_SUBJECT_FORMAT, serviceInfo, throwable.getClass().getSimpleName()),
                String.format("%tc  %s", new Date(), sw.toString().replaceAll("\\n", "<br/>"))));
    }

    @Async
    public void notifyByMail(Throwable throwable, String format, Object... args) {
        notifyByMail(throwable, String.format(format, args));
    }

   /* @Async
    //短信通知
    public void notifyByMobile(Throwable throwable, String message) {
        if (isIgnored(mobileIgnoredSet, throwable)) return;

        if (StringUtils.isEmpty(mobileRecipients)) {
            if (LOGGER.isInfoEnabled()) {
                LOGGER.debug("####Mobile recipients is empty.");
            }
            return;
        }

        smsClient.sendByMW(mobileRecipients, message);
    }*/

    @Async
    public void notifyByMobile(Throwable throwable, String format, String... args) {
        notifyByMobile(throwable, String.format(format, args));
    }

    @Async
    public void commonNotify(ExceptionContext context) {
        commonNotify(COMMON_ERROR_DESC, context);
    }

    @Async
    public void commonNotify(String description, ExceptionContext context) {
        Throwable throwable = context.getThrowable();

        String message = String.format("%s[%s]. Url:[%s]", description, serviceInfo, context.getUrl());
        notifyByMobile(throwable, message);

        notifyByMail(throwable, "%s<br/><br/>%s<br/>", message, context.getDumpInfo());
    }
}
